#version 450 core
#extension GL_NV_uniform_buffer_std430_layout : enable

layout(location = 0) in vec3 normal;
layout(location = 1) in vec3 fragPos;
layout(location = 0) out vec4 fragColor;
struct Scene {
    mat4 viewProjectionMatrix;
    vec3 cameraDir;
    vec3 cameraPos;
};
layout(binding = 0, std140) uniform SceneBlock {
    Scene scene;
};
struct DirLight {
    vec3 ambient, diffuse, specular, direction;
};
struct PointLight {
    vec3 ambient;
    float c0;
    vec3 diffuse;
    float c1;
    vec3 specular;
    float c2;
    vec3 position;
};
layout(binding = 2, std430) uniform LightBlock {
    uint numDirLights;
    uint numPointLights;
    layout(offset = 16) DirLight dirLight[4];
    PointLight pointLight[4];
};
struct MeshPhongMaterial {
    vec3 diffuse;
    vec3 specular;
    float shininess;
};
layout(binding = 3, std430) uniform MaterialBlock {
    MeshPhongMaterial material;
};

void main() {
    vec3 result = vec3(0.0);
    const vec3 viewDir = normalize(scene.cameraPos - fragPos);
    for (uint i = 0; i < numDirLights; ++i) {
        vec3 ambient = dirLight[i].ambient;
        vec3 lightDir = -normalize(dirLight[i].direction);
        vec3 diffuse = max(dot(lightDir, normal), 0.0f) * dirLight[i].diffuse * material.diffuse;
        vec3 reflectDir = reflect(-lightDir, normal);
        float spec = pow(max(dot(viewDir, reflectDir), 0.0f), material.shininess);
        vec3 specular = spec * dirLight[i].specular * material.specular;
        result += (ambient + diffuse + specular);
    }
    for (uint i = 0; i < numPointLights; ++i) {
        float dis = distance(pointLight[i].position, fragPos);
        float attenuation = pointLight[i].c0 + pointLight[i].c1 * dis + pointLight[i].c2 * dis * dis;
        vec3 ambient = pointLight[i].ambient;
        vec3 lightDir = normalize(pointLight[i].position - fragPos);
        vec3 diffuse = max(dot(lightDir, normal), 0.0f) * pointLight[i].diffuse;
        vec3 reflectDir = reflect(-lightDir, normal);
        float spec = pow(max(dot(viewDir, reflectDir), 0.0f), material.shininess);
        vec3 specular = spec * pointLight[i].specular * material.specular;
        result += (ambient + diffuse + specular) / attenuation;
    }
    fragColor = vec4(result, 1.0f);
}
